/* System includes */
#include <string.h>
#include <assert.h>
#include <stdlib.h>

/* OpenACN includes */
#include "opt.h"
#include "acnstdtypes.h"
#include "acn_port.h"
#include "acn_arch.h"
#include "acn_sdt.h"

#include "dmp.h"
#include "dmpmem.h"
#include "acn_dmp.h"
#include "rlp.h"
#include "sdt.h"
#include "acnlog.h"
#include "marshal.h"


#include "property.h"
#include "set_led.h"

#define LOG_FSTART() acnlog(LOG_DEBUG | LOG_PROP, "%s [", __func__)
#define LOG_FEND() acnlog(LOG_DEBUG | LOG_PROP, "%s ]", __func__)


dmp_property_map_t *all_property = NULL;

/*create a property and addto list property
Input : propertySize the size of property
Address : address of property
*/
dmp_property_map_t *property_new(uint32_t address,uint16_t size)
{

	if(property_findby_address(address)){
		acnlog(LOG_DEBUG | LOG_PROP, "property address duplicate");
		return NULL;
	}
	dmp_property_map_t *property = NULL;
	uint8_t *valuemem = NULL;
	//malloc memeory
	if ((valuemem = (uint8_t *)calloc(1, sizeof(uint8_t)*size)) == NULL) return NULL;
	if ((property = (dmp_property_map_t *)calloc(1, sizeof(dmp_property_map_t))) == NULL) return NULL;
	//allocate pointer
	property->property.address = address;
	property->property.ref_count = 0;
	property->size= size;
	property->value = valuemem;
	property->next = all_property;
	all_property = property;
	return property;
}

/*find property by address
 * Input :Address : address of property
 * Output: dmp_property_map_t struct for the property
*/
dmp_property_map_t *property_findby_address(uint32_t address)
{
	dmp_property_map_t *property = all_property;
	while(property != NULL){
		if(property->property.address == address) return property;
		property = property->next;
	}
	return NULL;
}

/*set property
 * Input :Address : address of property
 * 		  proterty_ptr : property value
 * Output: function status
*/
int set_property(uint32_t address,uint8_t *datap)
{
	dmp_property_map_t *property = NULL;
	uint8_t * value = NULL;
	int i;  /* for loop*/
	assert(datap);

	if((property = property_findby_address(address))==NULL)
		return FAIL;
	value = property->value;
	for (i=0; i<property->size; i++) {
		if(datap==NULL){
			acnlog(LOG_DEBUG | LOG_PROP, "propertyPtr size is not the same as property size ");
			return FAIL;
		}
	    *value++ = *datap++;
	}
	//todo  Only set led memory here , It in order to be a callback function
	set_led(property);
	return OK;
}


/*get property by address
 * input 	address:address of property
 * output 	property size
 * 		 	proterty_ptr for the value*/
uint16_t get_property_ptr(uint32_t address,uint8_t **ptr)
{
	dmp_property_map_t *property = NULL;
	if((property = property_findby_address(address))==NULL) return FAIL;
	//return the property
	*ptr = property->value;
	return property->size;
}

/* delete property by address
 * input 	address:address of property
 * output 	if ok return 0;
 * 		 	else return -1
 */
int property_deleteby_address(uint32_t address)
{
	dmp_property_map_t *property = all_property;
	dmp_property_map_t *previous = all_property;
	while(property != NULL){
		if(property->property.address == address) break;
		previous = property;
		property = property->next;
	}
	if(property == NULL) {
		acnlog(LOG_DEBUG | LOG_PROP, "no property found via address");
		return FAIL;
	}
	if(previous==property)
		all_property = property->next;
	else
		previous->next = property->next;
	property_release(property);
	property = NULL;
	return OK;
}


/*function to clean memory */
void property_close()
{
	dmp_property_map_t *property = all_property;
	dmp_property_map_t *next = NULL;
	while(property != NULL){
		next = property->next;
		property_release(property);
		property = next;
	}
	all_property = NULL;
	acnlog(LOG_DEBUG | LOG_PROP, "release all properties");
	return;
}


uint8_t *encode_property(dmp_property_map_t *property,uint8_t *datap)
{
	datap = marshalU16(datap, property->size);
	int i;
	for(i=0;i<property->size;i++){
		datap[i] = property->value[i];
	}
	return datap+property->size;
}

/* decode a property value form ptr
 * put the point into property
 * return the phrased num
 */
int decode_property(uint32_t address,uint8_t *datap,dmp_property_map_t **property)
{
	int i ;
	int size = 0;
	uint8_t *ptr = datap;
	size = unmarshalU16(datap);
	if(size <=0 ){
		acnlog(LOG_DEBUG | LOG_DMP,"Got the zero PropertyLen");
		return FAIL;
	}
	ptr += sizeof(uint16_t);
	if ((*property = (dmp_property_map_t *)calloc(1, sizeof(dmp_property_map_t))) == NULL)
		return FAIL;
	if (((*property)->value = (uint8_t *)calloc(1, sizeof(uint8_t)*size)) == NULL)
		return FAIL;
	//get the property value
	(*property)->property.address = address;
	(*property)->size = size;
	for(i =0 ; i<size ; i++){
		(*property)->value[i] = unmarshalU8(ptr++);
	}

	return ptr - datap;
}

void property_release(dmp_property_map_t *property)
{
	free(property->value);
	free(property);
}


void display_property(dmp_property_map_t *property)
{
	int i = 0;//for loop
	assert(property);

	acnlog(LOG_DEBUG | LOG_PROP, "===========================================================");
	acnlog(LOG_DEBUG | LOG_PROP, "property address %d",property->property.address);
	acnlog(LOG_DEBUG | LOG_PROP, "property size %d",property->size);
	acnlog(LOG_DEBUG | LOG_PROP, "property ref %d",property->property.ref_count);
	acnlog(LOG_DEBUG | LOG_PROP, "property value");
	for(i = 0 ;i< property->size;i++){
		acnlog(LOG_DEBUG | LOG_PROP, "property the %d th value is %d ",i,property->value[i]);
	}
	acnlog(LOG_DEBUG | LOG_PROP, "===========================================================");
}


void display_all()
{
	int count = 0;
	dmp_property_map_t *property = all_property;
	if(property==NULL) {
		acnlog(LOG_DEBUG | LOG_PROP, "===========================================================");
		acnlog(LOG_DEBUG | LOG_PROP, "no property here");
	}
	acnlog(LOG_DEBUG | LOG_PROP, "===========================================================");
	acnlog(LOG_DEBUG | LOG_PROP, "=================All properties are list here =============");
	while(property != NULL){
		acnlog(LOG_DEBUG | LOG_PROP, "===========================================================");
		display_property(property);
		property = property->next;
		count++;
	}
	acnlog(LOG_DEBUG | LOG_PROP, "=================totol properties are %d =============",count);
}












